﻿using Red.Core.UndoRedo;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Red.GameData.Actions
{
    /// <summary>
    /// Helper class for captured property values
    /// </summary>
    struct CapturedValueHelper
    {
        public Guid ValueId;
        public string Value;

        public CapturedValueHelper(Red.GameData.Raw.DataValue source)
        {
            ValueId = source.ID;
            Value = source.Value;
        }

        public void Restore( Raw.DataProperty prop )
        {
            prop.Action_AddValue( Value, ValueId );
        }
    }

    /// <summary>
    /// Helper class for captured property
    /// </summary>
    struct CapturedPropertyHelper
    {
        public List<CapturedValueHelper> Values;
        public List<Guid> SuppressedValues;

        public CapturedPropertyHelper(Red.GameData.Raw.DataProperty prop)
        {
            Values = new List<CapturedValueHelper>();
            foreach ( var sourceVal in prop.Values )
                Values.Add( new CapturedValueHelper(sourceVal) );


            SuppressedValues = new List<Guid>();
            foreach (var sourceId in prop.SupressedValues)
                SuppressedValues.Add(sourceId);
        }

        public bool Restore(Raw.DataObject obj, string propertyName)
        {
            var prop = obj.GetProperty(propertyName);
            if ( null == prop )
                return false;

            Debug.Assert( prop.Values.Count == 0 ); // we are not expecting values here, actuall the property should be brand new
            foreach ( var sourceVal in Values )
                sourceVal.Restore( prop );

            Debug.Assert( prop.SupressedValues.Count == 0 ); // we are not expecting values here, actuall the property should be brand new
            foreach ( var sourceId in SuppressedValues )
                prop.Action_SuppressValue( sourceId );

            return true;
        }
    }

    /// <summary>
    /// Remove override from property
    /// </summary>
    public class RemoveOverride : IAction
    {
        /// <summary>
        /// Target context of objects being edited
        /// </summary>
        private Red.GameData.EditorContext Context;

        /// <summary>
        /// Id of the object to modify
        /// </summary>
        private Guid ObjectID;

        /// <summary>
        /// Name of the property in the object to modify
        /// </summary>
        private string PropertyName;

        /// <summary>
        /// In case of array elements being targeted - the GUID of the value element, NOTE - this is always set for array elements
        /// </summary>
        private Guid ValueGuid;

        /// <summary>
        /// [UNDO] Previous value of the property, required so we know what to restore
        /// </summary>
        private CapturedPropertyHelper PreviousValue;

        /// <summary>
        /// Get user name for this action
        /// </summary>
        /// <returns></returns>
        string IAction.GetDisplayName()
        {
            return "Set value of '" + PropertyName + "'"; // TODO: more descriptive please :)
        }

        /// <summary>
        /// Execute action
        /// </summary>
        bool IAction.Do(Red.Core.UndoRedo.ActionHistory history, ref string errorMessage)
        {
            // get the target object
            var obj = Context.Data.Find(ObjectID);
            if (obj == null)
            {
                errorMessage = "Unable to retrieve target object: " + Red.GameData.Raw.Utils.FormatGuid(ObjectID);
                return false;
            }

            // create property holder
            var prop = obj.GetProperty(PropertyName);
            if (prop == null)
            {
                errorMessage = "Unable to retrieve property '" + PropertyName + "' for object: " + Red.GameData.Raw.Utils.FormatGuid(ObjectID);
                return false;
            }

            // set property value
            if (ValueGuid == Guid.Empty)
            {
                // NOTE: the object is modified
                obj.Action_RemovePropertyOverride(PropertyName);
            }
            else
            {
                // in case of array - remove the overridden value
                if (!prop.Action_RemoveValue(ValueGuid))
                {
                    errorMessage = "Unable to modify selected value for property '" + PropertyName + "' for object: " + Red.GameData.Raw.Utils.FormatGuid(ObjectID);
                    return false;
                }
            }

            // action was successful
            return true;
        }

        /// <summary>
        /// Revert execution of the action
        /// </summary>
        bool IAction.Undo(Red.Core.UndoRedo.ActionHistory history, ref string errorMessage)
        {
            // get the target object
            var obj = Context.Data.Find(ObjectID);
            if (obj == null)
            {
                errorMessage = "Unable to retrieve target object: " + Red.GameData.Raw.Utils.FormatGuid(ObjectID);
                return false;
            }

            // set property value
            if (ValueGuid == Guid.Empty)
            {
                // restore value
                PreviousValue.Restore( obj, PropertyName );
                return true;
            }
            else
            {
                // TODO TODO TODO
                // unimplemented
                return false;
            }
        }


        /// <summary>
        /// Construct simple action
        /// </summary>
        public static RemoveOverride Create(EditorContext context, Raw.DataObject obj, string propertyName, Guid valueID)
        {
            // we cannot "reset" non existing properties, so it must exist
            var prop = obj.GetPropertyNoCreate(propertyName);
            if (prop == null)
                return null; // no property to reset

            var ret = new RemoveOverride();
            ret.Context = context;
            ret.ObjectID = obj.ID;
            ret.ValueGuid = valueID;
            ret.PropertyName = propertyName;
            ret.PreviousValue = new CapturedPropertyHelper(prop); // capture state of the property

            // return created action
            return ret;
        }

    }
}
